﻿using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Xsl;
using System.Linq;
using HtmlAgilityPack;

namespace MetroLightKit.ConvertSvg
{

    public class Working
    {
        public event EventHandler<MessageWorkerArgs> ReturnMessageWorker;
        public bool CancelPending { get; set; }

        public string SvgDirectoryPath { get; set; }
        public string XslTransfromFile { get; set; }
        public string XamlResourceFileOut { get; set; }
        public string PrefixNameInXaml { get; set; }
        public string OutDirectoryPath { get; set; }

        public WebSite Site { get; set; }

        public enum Phase
        {
            Download,
            Convert
        };

        public Working()
        {
            XslTransfromFile = @".\xslt\svg2xaml.xsl";
        }

        #region Download
        public void DownloadSvgNodes()
        {
            OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Start, " --- Start Download SVG files--- "));
            string urlExe = "";
            try
            {
                foreach (var url in Site.GetUrls())
                {
                    if (CancelPending)
                        break;
                    OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Start, " --- Download {0} ", url));
                    urlExe = url;
                    WebRequest request = WebRequest.Create(url);
                    using (var response = (HttpWebResponse)request.GetResponse())
                    {
                        using (var dataStream = response.GetResponseStream())
                        {
                            if (dataStream != null)
                            {
                                using (var reader = new StreamReader(dataStream))
                                {
                                    ExtactSvgInHtml(reader);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Error, " -- Error : {0}  {1}  ", urlExe, ex.Message));
            }
            OnReturnMessageWorker(new MessageWorkerArgs(CancelPending ? TypeMessage.Cancel : TypeMessage.End, " --- End Download SVG files--- "));
            CancelPending = false;
        }
        public void ExtactSvgInHtml(StreamReader html)
        {
            var outDirectory = new DirectoryInfo(SvgDirectoryPath);
            if (!outDirectory.Exists)
                outDirectory.Create();

            var doc = new HtmlDocument();
            
            doc.Load(html);

            var col = doc.DocumentNode.SelectNodes(Site.XpathSvgs);

            InitCounter(col.Count);
            int i = 0;
            foreach (var node in col)
            {
                if (CancelPending)
                    break;
                i++;
                try
                {
                    var svg = node.OuterHtml;
                    //var name = node.Ancestors().First().SelectSingleNode(@".//a[@class='noun-name']").InnerText;
                    var name = node.SelectSingleNode(Site.XpathName).InnerText;

                    using (var file = File.CreateText(Path.Combine(outDirectory.FullName, name + ".svg")))
                    {
                        file.Write(svg);
                        OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Information, GetPercent(i), "> {0}.svg", name));
                    }
                }
                catch (Exception ex)
                {
                    OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Error, " ---{0}--- ", ex.Message));
                }
            }
        }
        #endregion

        #region Convert 
        public void ConvertSvgToXamlResource()
        {
            OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Start, " --- Start Convert SVG files--- "));
            try
            {
                var files = Directory.GetFiles(SvgDirectoryPath, "*.svg");
                InitCounter(files.Length);

                for (int i = 0; i < files.Length; i++)
                {
                    if (CancelPending)
                        break;

                    var file = files[i];
                    ConvertSvgToXamlResourceWithXsl(file);
                    int x = GetPercent(i);
                    OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Information, x, " > {0} ", file));
                }
            }
            catch (Exception ex)
            {
                OnReturnMessageWorker(new MessageWorkerArgs(TypeMessage.Error, " -- Error : {0}    ", ex.Message));
            }
            OnReturnMessageWorker(new MessageWorkerArgs(CancelPending? TypeMessage.Cancel: TypeMessage.End, " --- End Convert SVG files--- "));
            CancelPending = false;
        }

        private void ConvertSvgToXamlResourceWithXsl(string svgFilePath)
        {
            // XML Settings    
            var xmlSettings = new XmlReaderSettings
            {
                XmlResolver = null,
                IgnoreComments = true,
                DtdProcessing = DtdProcessing.Ignore,
                ValidationType = ValidationType.None
            };

            xmlSettings.ValidationEventHandler += (sender, e) => OnReturnMessageWorker(
                new MessageWorkerArgs(TypeMessage.Error, "{0}({1},{2}): {3} - {4}",
                                      e.Exception.SourceUri,
                                      e.Exception.LineNumber,
                                      e.Exception.LinePosition,
                                      e.Severity, e.Message));
            

            // XSLT Settings    
            var xsltSettings = new XmlReaderSettings
            {
                XmlResolver = null,
                DtdProcessing = DtdProcessing.Ignore,
                ValidationType = ValidationType.None
            };

            xsltSettings.ValidationEventHandler += (sender, e) => OnReturnMessageWorker(
                new MessageWorkerArgs(TypeMessage.Error, "{0}({1},{2}): {3} - {4}",
                                      e.Exception.SourceUri,
                                      e.Exception.LineNumber,
                                      e.Exception.LinePosition,
                                      e.Severity, e.Message));

            // Init params    
            //XsltArgumentList xslArgs = new XsltArgumentList();    
            //if (parameters != null)
            //{
            //    foreach (KeyValuePair<string, string> param in parameters)            
            //        xslArgs.AddParam(param.Key, string.Empty, param.Value);
            //}


            using (var reader = XmlReader.Create(svgFilePath, xmlSettings))
            {
                var xslSettings = new XsltSettings(true, true);
                using (var xslSource = XmlReader.Create(XslTransfromFile, xsltSettings))
                {
                    var xsltDoc = new XslCompiledTransform();
                    xsltDoc.Load(xslSource, xslSettings, new XmlUrlResolver());
                    using (var sw = new StringWriter())
                    {
                        var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, OmitXmlDeclaration = true };
                        using (var xw = XmlWriter.Create(sw, settings))
                        {
                            xsltDoc.Transform(reader, /*xslArgs,*/ xw);
                            if (!string.IsNullOrEmpty(XamlResourceFileOut))
                            {
                                const string nameSpace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
                                const string nameSpace2 = "http://schemas.microsoft.com/winfx/2006/xaml";
                                const string nameSpace3 = "clr-namespace:MetroLightKit.Controls;assembly=MetroLightKit";
                                var doc = new XmlDocument();
                                if (File.Exists(XamlResourceFileOut))
                                    doc.Load(XamlResourceFileOut);
                                else
                                {
                                    doc.AppendChild(doc.CreateElement("ResourceDictionary", nameSpace));

                                }
                                if (doc.DocumentElement != null)
                                {
                                    doc.DocumentElement.SetAttribute("xmlns:x", nameSpace2);
                                    doc.DocumentElement.SetAttribute("xmlns:Metro", nameSpace3);

                                    var elem = doc.CreateElement("Style", nameSpace);
                                    elem.SetAttribute("TargetType", "Metro:MetroButtonContent");
                                    var name = PrefixNameInXaml + Path.GetFileNameWithoutExtension(svgFilePath).Trim().Replace(" ", "");

                                    elem.SetAttribute("Name", nameSpace2, name);
                                    var node = doc.DocumentElement.AppendChild(elem);

                                    elem = doc.CreateElement("Setter", nameSpace);
                                    elem.SetAttribute("Property", "Template");
                                    node = node.AppendChild(elem);

                                    elem = doc.CreateElement("Setter.Value", nameSpace);
                                    node = node.AppendChild(elem);

                                    elem = doc.CreateElement("ControlTemplate", nameSpace);
                                    elem.SetAttribute("TargetType", "Metro:MetroButtonContent");
                                    node = node.AppendChild(elem);

                                    var reg = new Regex("=\"[^\"]+(px)\"");
                                    var text = reg.Replace(sw.ToString(), match => match.Value.Replace("px", ""));
                                    reg = new Regex("(xmlns:[^=]*=\"[^\"]+\")|(Fill=\"[^\"]+\")|(Name=\"[^\"]+\")|(Canvas.Top=\"0\")|(Canvas.Left=\"0\")");
                                    text = reg.Replace(text, "");

                                    var frag = doc.CreateDocumentFragment();
                                    frag.InnerXml = text;
                                    var canvas = doc.CreateElement("Canvas", nameSpace);
                                    
                                    PopulateElement(canvas, frag.SelectNodes(".//@Canvas.Top"));
                                    PopulateElement(canvas, frag.SelectNodes(".//@Canvas.Left"));

                                    if (canvas.ChildNodes.Count > 0)
                                        frag.FirstChild.AppendChild(canvas);

                                    if (frag.FirstChild != null)
                                    {
                                        if (frag.FirstChild.Attributes != null)
                                            frag.FirstChild.Attributes.RemoveNamedItem("xmlns");
                                        node.AppendChild(frag.FirstChild);
                                    }
                                    doc.Save(XamlResourceFileOut);
                                }
                            }
                        }
                    }
                }
            }
        }

        private void PopulateElement(XmlNode canvas, IEnumerable colAttributes)
        {
            if (colAttributes != null)
                foreach (var attrib in colAttributes.Cast<XmlAttribute>().Where(attrib => attrib.OwnerElement != null))
                {
                    canvas.AppendChild(attrib.OwnerElement.CloneNode(true));
                    if (attrib.OwnerElement.ParentNode != null)
                        attrib.OwnerElement.ParentNode.RemoveChild(attrib.OwnerElement);
                }
        }
        #endregion

        private void OnReturnMessageWorker(MessageWorkerArgs e)
        {
            var handler = ReturnMessageWorker;
            if (handler != null) handler(this, e);
        }

        private double _percentScale = 0;
        public void InitCounter(int count)
        {
            _percentScale = count;
            if (_percentScale > 0)
                _percentScale = _percentScale / 100;
        }
        public int GetPercent(int iCount)
        {
            return (int) (iCount/_percentScale);
        }
    }

}
